package com.peas.platform.module.icims.budget.apply.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import com.peas.platform.common.core.enums.BaseEnum;
import com.peas.platform.framework.mybatisplus.domain.BaseDomain;
import com.peas.platform.framework.mybatisplus.po.BasePo;
import com.peas.platform.framework.mybatisplus.service.BaseServiceImpl;
import com.peas.platform.module.icims.base.approve.component.ApproveCallback;
import com.peas.platform.module.icims.base.approve.dto.ApproveDto;
import com.peas.platform.module.icims.base.approve.dto.ApproveOrderNodeDto;
import com.peas.platform.module.icims.base.approve.enums.ApproveOrderBusinessType;
import com.peas.platform.module.icims.base.approve.service.ApproveOrderService;
import com.peas.platform.module.icims.base.approve.service.ApproveService;
import com.peas.platform.module.icims.base.mdm.dto.GovernmentDto;
import com.peas.platform.module.icims.base.mdm.service.GovernmentService;
import com.peas.platform.module.icims.budget.apply.api.req.SaveBudgetTwoVo;
import com.peas.platform.module.icims.budget.apply.api.req.SubmitBudgetTwoVo;
import com.peas.platform.module.icims.budget.apply.controller.vo.*;
import com.peas.platform.module.icims.budget.apply.domain.BudgetTwoDomain;
import com.peas.platform.module.icims.budget.apply.dto.*;
import com.peas.platform.module.icims.budget.apply.enums.BudgetState;
import com.peas.platform.module.icims.budget.apply.service.*;
import com.peas.platform.module.icims.budget.archives.dto.BudgetKpiDto;
import com.peas.platform.module.icims.budget.archives.enums.KpiSource;
import com.peas.platform.module.icims.budget.archives.service.BudgetKpiService;
import com.peas.platform.module.icims.budget.project.dto.BudgetProjectTwoDto;
import com.peas.platform.module.icims.budget.project.enums.BudgetProjectTwoLibraryState;
import com.peas.platform.module.icims.budget.project.enums.BudgetProjectTwoState;
import com.peas.platform.module.icims.budget.project.service.BudgetProjectTwoService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;

@Service
public class BudgetTwoServiceImpl extends BaseServiceImpl<BudgetTwoDto> implements BudgetTwoService, ApproveCallback {

    @Resource
    private BudgetTwoDomain budgetTwoDomain;
    @Resource
    private BudgetOneService budgetOneService;
    @Resource
    private BudgetProjectTwoService budgetProjectTwoService;
    @Resource
    private BudgetTwoPurchaseService budgetTwoPurchaseService;
    @Resource
    private BudgetTwoItemService budgetTwoItemService;
    @Resource
    private BudgetTwoYearService budgetTwoYearService;
    @Resource
    private BudgetTwoKpiService budgetTwoKpiService;
    @Resource
    private BudgetTwoFileService budgetTwoFileService;
    @Resource
    private ApproveService approveService;
    @Resource
    private ApproveOrderService approveOrderService;
    @Resource
    private GovernmentService governmentService;
    @Resource
    private BudgetKpiService budgetKpiService;

    @Override
    public BaseDomain<BudgetTwoDto, ? extends BasePo> getDomain() {
        return budgetTwoDomain;
    }

    private BudgetTwoDto getOneByBudgetOneId(Long budgetOneId) {
        Assert.notNull(budgetOneId, "一上预算编制ID不能为空");
        BudgetTwoDto c = new BudgetTwoDto();
        c.setBudgetOneId(budgetOneId);
        return this.getOne(c, false);
    }

    @Transactional
    @Override
    public BudgetTwoDto saveBudgetTwo(SaveBudgetTwoVo vo) {
        BudgetTwoDto btd = BeanUtil.copyProperties(vo, BudgetTwoDto.class);
        if (StrUtil.isBlank(btd.getState())) {
            btd.setState(BudgetState.save.getKey());
        }
        Assert.notNull(vo.getBudgetOneId(), "请选择一上预算编制");
        BudgetOneDto budgetOneDto = budgetOneService.getById(vo.getBudgetOneId());
        Assert.notNull(budgetOneDto, "一上预算编制不存在");
        vo.setApplyOneFund(budgetOneDto.getApplyOneFund());
        vo.setApproveOneFund(budgetOneDto.getApproveOneFund());
        vo.setApplyTwoFund(null);
        vo.setApproveTwoFund(null);
        int result;
        if (vo.getId() != null) {
            BudgetTwoDto bean = this.getById(vo.getId());
            BudgetState state = BaseEnum.key2Enum(BudgetState.class, bean.getState());
            Assert.isTrue(BudgetState.save == state || BudgetState.revocation == state || BudgetState.reject == state, "二上预算编制[" + bean.getCode() + "]的状态为[" + state.getKey() + "-" + state.getValue() + "]，不允许修改");
            result = this.updateById(btd);
        } else {
            BudgetTwoDto bean = this.getOneByBudgetOneId(vo.getBudgetOneId());
            if (bean != null) {
                BudgetState state = BaseEnum.key2Enum(BudgetState.class, bean.getState());
                Assert.isTrue(BudgetState.save == state || BudgetState.revocation == state || BudgetState.reject == state, "二上预算编制[" + bean.getCode() + "]的状态为[" + state.getKey() + "-" + state.getValue() + "]，不允许修改");
                btd.setId(bean.getId());
                btd.setVersion(bean.getVersion());
                result = this.updateById(btd);
            } else {
                result = this.save(btd);
            }
        }
        Assert.isTrue(result > 0, "保存失败");
        List<BudgetTwoPurchaseDto> btpds = budgetTwoPurchaseService.listByField("budgetTwoId", btd.getId());
        budgetTwoPurchaseService.removeBatchByList(btpds);
        List<BudgetTwoItemDto> btids = budgetTwoItemService.listByField("budgetTwoId", btd.getId());
        budgetTwoItemService.removeBatchByList(btids);
        List<BudgetTwoYearDto> btyds = budgetTwoYearService.listByField("budgetTwoId", btd.getId());
        budgetTwoYearService.removeBatchByList(btyds);
        List<BudgetTwoKpiDto> btkds = budgetTwoKpiService.listByField("budgetTwoId", btd.getId());
        budgetTwoKpiService.removeBatchByList(btkds);
        List<BudgetTwoFileDto> btfds = budgetTwoFileService.listByField("budgetTwoId", btd.getId());
        budgetTwoFileService.removeBatchByList(btfds);
        // purchases
        if (CollUtil.isNotEmpty(vo.getPurchases())) {
            List<BudgetTwoPurchaseDto> btpdList = new ArrayList<>();
            CollUtil.forEach(vo.getPurchases(), (CollUtil.Consumer<BudgetTwoPurchaseVo>) (value, index) -> {
                BudgetTwoPurchaseDto dto = BeanUtil.copyProperties(value, BudgetTwoPurchaseDto.class);
                dto.setBudgetTwoId(btd.getId());
                btpdList.add(dto);
            });
            budgetTwoPurchaseService.saveBatch(btpdList);
        }
        // items
        if (CollUtil.isNotEmpty(vo.getItems())) {
            List<BudgetTwoItemDto> btidList = new ArrayList<>();
            CollUtil.forEach(vo.getItems(), (CollUtil.Consumer<BudgetTwoItemVo>) (value, index) -> {
                BudgetTwoItemDto dto = BeanUtil.copyProperties(value, BudgetTwoItemDto.class);
                dto.setBudgetTwoId(btd.getId());
                btidList.add(dto);
            });
            budgetTwoItemService.saveBatch(btidList);
        }
        // years
        if (CollUtil.isNotEmpty(vo.getYears())) {
            List<BudgetTwoYearDto> btydList = new ArrayList<>();
            CollUtil.forEach(vo.getYears(), (CollUtil.Consumer<BudgetTwoYearVo>) (value, index) -> {
                BudgetTwoYearDto dto = BeanUtil.copyProperties(value, BudgetTwoYearDto.class);
                dto.setBudgetTwoId(btd.getId());
                btydList.add(dto);
            });
            budgetTwoYearService.saveBatch(btydList);
        }
        // kpis
        List<BudgetTwoKpiVo> kpis = vo.getKpis();
        if (CollUtil.isNotEmpty(kpis)) {
            CollUtil.forEach(kpis, (CollUtil.Consumer<BudgetTwoKpiVo>) (one, i1) -> {
                if (one.getParentId() == null) {
                    BudgetTwoKpiDto dto1 = BeanUtil.copyProperties(one, BudgetTwoKpiDto.class);
                    dto1.setBudgetTwoId(btd.getId());
                    budgetTwoKpiService.save(dto1);
                    CollUtil.forEach(kpis, (CollUtil.Consumer<BudgetTwoKpiVo>) (two, i2) -> {
                        if (two.getParentId() != null && two.getParentId().compareTo(one.getId()) == 0) {
                            BudgetTwoKpiDto dto2 = BeanUtil.copyProperties(two, BudgetTwoKpiDto.class);
                            dto2.setBudgetTwoId(btd.getId());
                            dto2.setParentId(dto1.getId());
                            budgetTwoKpiService.save(dto2);
                            CollUtil.forEach(kpis, (CollUtil.Consumer<BudgetTwoKpiVo>) (three, i3) -> {
                                if (three.getParentId() != null && three.getParentId().compareTo(two.getId()) == 0) {
                                    BudgetTwoKpiDto dto3 = BeanUtil.copyProperties(three, BudgetTwoKpiDto.class);
                                    dto3.setBudgetTwoId(btd.getId());
                                    dto3.setParentId(dto2.getId());
                                    if (StrUtil.isBlank(dto3.getKpiCode())) {
                                        BudgetKpiDto kpi = budgetKpiService.addBudgetKpi(KpiSource.budgetTwo, dto3.getKpiName(), two.getId(), dto3.getWeight());
                                        if (kpi != null)
                                            dto3.setKpiCode(kpi.getCode());
                                    }
                                    budgetTwoKpiService.save(dto3);
                                }
                            });
                        }
                    });
                }
            });
        }
        // files
        if (CollUtil.isNotEmpty(vo.getFiles())) {
            List<BudgetTwoFileDto> btfdList = new ArrayList<>();
            CollUtil.forEach(vo.getFiles(), (CollUtil.Consumer<BudgetTwoFileVo>) (value, index) -> {
                BudgetTwoFileDto dto = BeanUtil.copyProperties(value, BudgetTwoFileDto.class);
                dto.setBudgetTwoId(btd.getId());
                btfdList.add(dto);
            });
            budgetTwoFileService.saveBatch(btfdList);
        }
        return btd;
    }

    @Transactional
    @Override
    public void submitBudgetTwo(SubmitBudgetTwoVo vo) {
        Assert.notNull(vo.getApproveId(), "请选择审批模版");
        SaveBudgetTwoVo v = BeanUtil.copyProperties(vo, SaveBudgetTwoVo.class);
        v.setState(BudgetState.submit.getKey());
        v.setItems(vo.getItems());
        v.setKpis(vo.getKpis());
        BudgetTwoDto btd = this.saveBudgetTwo(v);
        ApproveDto ad = approveService.getById(vo.getApproveId());
        approveOrderService.createApproveOrder(ad, BeanUtil.copyToList(vo.getApproveNodes(), ApproveOrderNodeDto.class), ApproveOrderBusinessType.budgetTwo, btd.getId(), btd.getCode(), null, this.getClass());
        BudgetProjectTwoDto ptd = budgetProjectTwoService.getById(btd.getProjectTwoId());
        ptd.setLibraryState(BudgetProjectTwoLibraryState.construction.getKey());
        budgetProjectTwoService.updateById(ptd);
    }

    @Transactional
    @Override
    public void revocationBudgetTwo(Long id) {
        BudgetTwoDto dto = this.getById(id);
        Assert.notNull(dto, "找不到预算编制");
        dto.setState(BudgetState.revocation.getKey());
        this.updateById(dto);
        approveOrderService.revocationApproveOrder(ApproveOrderBusinessType.budgetTwo, id);
    }

    @Override
    public List<BudgetTwoDto> queryBudgetTwoById(List<Long> idList) {
        List<BudgetTwoDto> list = this.listByIds(idList);
        CollUtil.forEach(list, (CollUtil.Consumer<BudgetTwoDto>) (value, index) -> calcBudgetTwoTotalAmount(value));
        return list;
    }

    @Override
    public List<BudgetTwoDto> queryBudgetTwoByOrganizationId(Long organizationId) {
        //TODO
        return null;
    }

    @Override
    public List<BudgetTwoDto> queryBudgetTwoByProjectOneId(Long projectOneId) {
        List<BudgetTwoDto> list = new ArrayList<>();
        List<BudgetProjectTwoDto> ptds = budgetProjectTwoService.listByField("projectOneId", projectOneId);
        CollUtil.forEach(ptds, (CollUtil.Consumer<BudgetProjectTwoDto>) (v1, i1) -> {
            List<BudgetTwoDto> btds = listByField("projectTwoId", v1.getId());
            CollUtil.forEach(btds, (CollUtil.Consumer<BudgetTwoDto>) (v2, i2) -> {
                calcBudgetTwoTotalAmount(v2);
                list.add(v2);
            });
        });
        return list;
    }

    @Override
    public List<BudgetTwoDto> queryBudgetTwoByApplyGovernmentId(Long applyGovernmentId) {
        List<BudgetTwoDto> list = new ArrayList<>();
        List<GovernmentDto> gds = governmentService.queryGovernmentAndSubs(applyGovernmentId);
        CollUtil.forEach(gds, (CollUtil.Consumer<GovernmentDto>) (v1, i1) -> {
            List<BudgetTwoDto> btds = listByField("applyGovId", v1.getId());
            CollUtil.forEach(btds, (CollUtil.Consumer<BudgetTwoDto>) (v2, i2) -> {
                calcBudgetTwoTotalAmount(v2);
                list.add(v2);
            });
        });
        return list;
    }

    @Override
    public List<BudgetTwoDto> queryBudgetTwoByUseGovernmentId(Long useGovernmentId) {
        List<BudgetTwoDto> list = new ArrayList<>();
        List<GovernmentDto> gds = governmentService.queryGovernmentAndSubs(useGovernmentId);
        CollUtil.forEach(gds, (CollUtil.Consumer<GovernmentDto>) (v1, i1) -> {
            List<BudgetTwoDto> btds = listByField("useGovId", v1.getId());
            CollUtil.forEach(btds, (CollUtil.Consumer<BudgetTwoDto>) (v2, i2) -> {
                calcBudgetTwoTotalAmount(v2);
                list.add(v2);
            });
        });
        return list;
    }

    @Override
    public BigDecimal calcBudgetTwoTotalAmount(BudgetTwoDto dto) {
        if (dto == null) {
            return BigDecimal.ZERO;
        }
        AtomicReference<BigDecimal> all = new AtomicReference<>(BigDecimal.ZERO);
        List<BudgetTwoItemDto> btids = budgetTwoItemService.listByField("budgetTwoId", dto.getId());
        CollUtil.forEach(btids, (CollUtil.Consumer<BudgetTwoItemDto>) (btid, index) -> {
            BigDecimal sum = all.get().add(btid.getPrice().multiply(btid.getNum()));
            all.set(sum);
        });
        dto.setTotalAmount(all.get());
        return all.get();
    }

    @Transactional
    @Override
    public void adjustBudgetTwoAmount(List<BudgetTwoVo> btvs) {
        CollUtil.forEach(btvs, (CollUtil.Consumer<BudgetTwoVo>) (v, index) -> {
            BudgetTwoDto btd = getById(v.getId());
            btd.setApproveTwoFund(v.getApproveTwoFund());
            btd.setState(BudgetState.complete.getKey());
            BigDecimal applyTwoFund = calcBudgetTwoTotalAmount(btd);
            btd.setApplyTwoFund(applyTwoFund);
            updateById(btd);
            BudgetProjectTwoDto ptd = budgetProjectTwoService.getById(btd.getProjectTwoId());
            ptd.setState(BudgetProjectTwoState.budgetTwoComplete.getKey());
            ptd.setLibraryState(BudgetProjectTwoLibraryState.execute.getKey());
            budgetProjectTwoService.updateById(ptd);
        });
    }

    @Transactional
    @Override
    public void callbackAfterApprove(Long businessId, boolean isPass, boolean isFinalPass) {
        if (businessId == null)
            return;
        if (isPass) {
            if (isFinalPass) {
                BudgetTwoDto btd = this.getById(businessId);
                btd.setState(BudgetState.pass.getKey());
                this.updateById(btd);
                BudgetProjectTwoDto ptd = budgetProjectTwoService.getById(btd.getProjectTwoId());
                ptd.setState(BudgetProjectTwoState.budgetTwoApproved.getKey());
                budgetProjectTwoService.updateById(ptd);
            }
        } else {
            BudgetTwoDto btd = this.getById(businessId);
            btd.setState(BudgetState.reject.getKey());
            this.updateById(btd);
            BudgetProjectTwoDto ptd = budgetProjectTwoService.getById(btd.getProjectTwoId());
            ptd.setLibraryState(BudgetProjectTwoLibraryState.alternative.getKey());
            budgetProjectTwoService.updateById(ptd);
        }
    }

}
